package com.atguigu.tingshu.order.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.album.client.AlbumClientFeign;
import com.atguigu.tingshu.album.client.TrackClientFeign;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.config.PrivateKeyConfigInt;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.payment.client.PaymentFeignClient;
import com.atguigu.tingshu.user.client.UserClientFeign;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaSigner;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private UserClientFeign userClientFeign;

    @Autowired
    private AlbumClientFeign albumClientFeign;

    @Autowired
    private PrivateKeyConfigInt privateKeyConfigInt;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private OrderDerateMapper orderDerateMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private TrackClientFeign trackClientFeign;

    @Autowired
    private PaymentFeignClient paymentFeignClient;


    /**
     * 订单确认
     *
     * @param tradeVo
     */
    @Override
    public Object trade(TradeVo tradeVo) {
        //初始化OrderInfoVo对象
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        //获取用户的支付类型 1001-专辑 1002-声音 1003-vip会员
        String itemType = tradeVo.getItemType();
        switch (itemType) {
            //专辑订单
            case "1001" -> {
                orderInfoVo = initAlbumOrder(tradeVo.getItemId());

            }
            //声音订单
            case "1002" -> {
                orderInfoVo = initTrackOrder(tradeVo.getItemId());

            }
            //会员订单
            case "1003" -> {
                orderInfoVo = initVipOrder(tradeVo.getItemId());
            }
        }
        //时间戳
        orderInfoVo.setTimestamp(System.currentTimeMillis());

        //生成签名,将orderInfoVo包装进签名，
        Jwt encode = JwtHelper.encode(JSONObject.toJSONString(orderInfoVo), new RsaSigner(privateKeyConfigInt.privateKey()));
        orderInfoVo.setSign(encode.getEncoded());
        return orderInfoVo;
    }


    /**
     * 订单提交
     *
     * @param orderInfoVo
     * @return
     */
    @Override
    public Object submitOrder(OrderInfoVo orderInfoVo) {
        //初始化OrderInfo对象
        OrderInfo orderInfo = new OrderInfo();
        //获取签名
        String sign = orderInfoVo.getSign();
        try {
            if (sign != null) {
                //检验签名
                Jwt decoded = JwtHelper.decodeAndVerify(sign, new RsaVerifier(SystemConstant.PUBLIC_KEY));
                //获取载荷
                String claims = decoded.getClaims();
                //反序列化,得到订单信息
                OrderInfoVo orderInfoVo1 = JSONObject.parseObject(claims, OrderInfoVo.class);
                //判断创建订单是否超过5分钟
                orderInfo.setItemType(orderInfoVo1.getItemType());
                Long timestamp = orderInfoVo1.getTimestamp();
                if ((System.currentTimeMillis() - timestamp) >= 5 * 60000) {
                    throw new GuiguException(201, "页面停留时间过长，请重新下单");
                }
                //对订单内容重复校验 a:限制类型 b:限制具体内容
                //a:限制类型
                ;
                if (!checkOrderInfoA(orderInfoVo1)) {
                    throw new GuiguException(201, "重复下单!");
                }

                //将订单信息写进数据库A
                BeanUtils.copyProperties(orderInfoVo1, orderInfo);
                //补全orderinfo对象信息
                orderInfo.setUserId(AuthContextHolder.getUserId());

                switch (orderInfoVo1.getItemType()) {
                    //付款项目类型: 1001-专辑 1002-声音 1003-vip会员"
                    case "1001" -> orderInfo.setOrderTitle("购买专辑");
                    case "1002" -> orderInfo.setOrderTitle("购买声音");
                    case "1003" -> orderInfo.setOrderTitle("购买会员");
                }
                orderInfo.setOrderNo(orderInfoVo1.getTradeNo());
                orderInfo.setOrderStatus("0901");

                //保存orderinfo
                if (save(orderInfo)) {
                    //获取订单id
                    Long orderId = orderInfo.getId();
                    saveOrderDetail(orderInfoVo1.getOrderDetailVoList(), orderId);
                    saveOrderDerate(orderInfoVo1.getOrderDerateVoList(), orderId);


                    //提交订单开始计时
                    rabbitTemplate.convertAndSend("OrderInfo_Cancel_Exchange", "selfCancel", orderInfo.getUserId() + ":" + orderInfo.getOrderNo(),
                            message -> {
                                MessageProperties messageProperties = message.getMessageProperties();
                                messageProperties.setExpiration(30 * 1000 + "");
                                return message;
                            });

                    //初始化返回对象
                    JSONObject result = new JSONObject();
                    //将订单号返回
                    result.put("orderNo", orderInfo.getOrderNo());
                    return result;
                }
            }
        } catch (Exception e) {
            releaseLock(orderInfoVo.getOrderDetailVoList().get(0).getItemId(),
                    orderInfoVo.getItemType(),
                    AuthContextHolder.getUserId());
            throw e;
        }
        return null;
    }

    /**
     * 释放锁
     *
     * @param itemId
     * @param itemType
     * @param userId
     */
    private void releaseLock(Long itemId, String itemType, Long userId) {
        if (itemType.equals("1002")) {
            itemId = trackClientFeign.getTrackInfoDetail(itemId).getAlbumId();
        }
        //自动释放锁
        redisTemplate.expire("SubmitOrder_" + userId + "_" + itemType + "_" + itemId, 5, TimeUnit.SECONDS);
        //手动释放锁
        redisTemplate.delete("SubmitOrder_" + userId + "_" + itemType + "_" + itemId);
    }

    /**
     * 超时订单取消
     *
     * @param orderNo
     */
    @Override
    public void cancelOrder(String orderNo) {
        //默认是主动取消
        String status = "0903";
        //if本地线程没有用户id，说明是超时取消
        Long userId = AuthContextHolder.getUserId();
        if (userId == null) {
            status = "0904";//超时取消

            //用冒号分割orderno
            String[] split = orderNo.split(":");
            //获取用户id和订单号
            userId = Long.valueOf(split[0]);
            orderNo = split[1];
        }

        //加锁
        RLock lock = redissonClient.getLock("OrderInfoCancel_" + userId + "OrderNo_" + orderNo);
        try {
            if (lock.tryLock()) {
                //取消订单
                OrderInfo orderInfo = getOne(new LambdaQueryWrapper<OrderInfo>()
                        .eq(OrderInfo::getUserId, userId)//指定用户
                        .eq(OrderInfo::getOrderNo, orderNo)//指定订单
                        .eq(OrderInfo::getOrderStatus, "0901"));//未支付状态

                //非空判断
                if (orderInfo == null) {
                    //说明没有数据,结束
                    return;
                }
                //修改订单状态
                orderInfo.setOrderStatus(status);
                if (!updateById(orderInfo)) {
                    //取消订单失败
                    throw new GuiguException(201, "取消订单失败，请重新再试");
                }
                //查询itemId
                Long itemId = orderDetailMapper.selectItemIdByOrderId(orderInfo.getId());
                //清楚Redis中放进去的锁标识
                releaseLock(itemId, orderInfo.getItemType(), userId);
            }
        } catch (Exception e) {
            throw e;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 查询用户订单列表
     *
     * @param orderInfoPage
     * @return
     */
    @Override
    public Page findUserPage(Page<OrderInfo> orderInfoPage) {
        //获取用户id
        Long userId = AuthContextHolder.getUserId();
        Page<OrderInfo> ListOrderInfoPage = orderInfoMapper.selectPage(orderInfoPage, new LambdaQueryWrapper<OrderInfo>()
                .eq(OrderInfo::getUserId, userId));
        return ListOrderInfoPage;
    }

    //获取订单详情
    @Override
    public OrderInfo getOrderInfo(String orderNo) {
        //获取当前用户
        Long userId = AuthContextHolder.getUserId();

        //查询订单详情表
        OrderInfo orderInfo = orderInfoMapper.selectOne(new LambdaQueryWrapper<OrderInfo>().eq(OrderInfo::getUserId, userId).eq(OrderInfo::getOrderNo, orderNo));
        Long orderInfoId = orderInfo.getId();
        //查询订单明细
        List<OrderDetail> orderDetails = orderDetailMapper.selectList(new LambdaQueryWrapper<OrderDetail>().eq(OrderDetail::getOrderId, orderInfoId));
        //查询订单减免表
        List<OrderDerate> orderDerates = orderDerateMapper.selectList(new LambdaQueryWrapper<OrderDerate>().eq(OrderDerate::getOrderId, orderInfoId));
        orderInfo.setOrderDetailList(orderDetails);
        orderInfo.setOrderDerateList(orderDerates);
        return orderInfo;
    }

    /**
     * 订单支付，支付渠道，订单号
     * @param payway
     * @param orderNo
     * @return
     */
    @Override
    public Object getOrderPayInfo(String payway, String orderNo) {
        if (StringUtils.isEmpty(payway) || StringUtils.isEmpty(orderNo)) {
            throw new GuiguException(201, "参数错误");
        }
        //获取当前用户
        Long userId = AuthContextHolder.getUserId();
        //解决重复申请问题-> 一个用户一个订单 10min 只能生成一个支付地址
        //加锁
        //1、 先去Redis查，没有的话再去申请
        String  codeUrl =(String) redisTemplate.opsForValue().get("Order_Payinfo_Lock_User" + userId + "OrderNo_" + orderNo +"payway"+payway);
        if (!StringUtils.isEmpty(codeUrl)){
            return codeUrl;
        }
        // //同一个用户同一笔订单申请一次,限制十分钟
        RLock lock = redissonClient.getLock("Order_Payinfo_Lock_User" + userId + "OrderNo_" + orderNo);
        if (lock.tryLock()){
            try{
                OrderInfo orderInfo = orderInfoMapper.selectOne(new LambdaQueryWrapper<OrderInfo>()
                        .eq(OrderInfo::getOrderNo, orderNo)//指定订单
                        .eq(OrderInfo::getUserId, userId)//当前用户
                        .eq(OrderInfo::getOrderStatus, "0901"));//而且是未支付的
                if (orderInfo == null) {
                    throw new GuiguException(201, "订单不存在");
                }
                //选择支付渠道
                switch (orderInfo.getPayWay()) {
                    case "1101" ->{
                        Map<String,String> params = new HashMap<>();
                        params.put("userId",userId.toString());
                        params.put("type","1301");
                        params.put("orderNo",orderNo);
                        params.put("money",orderInfo.getOrderAmount().toString());
                        params.put("desc",orderInfo.getOrderTitle());
                        params.put("exchange","paymentInfo_Exchange");
                        params.put("routindKey","OrderInfo_Queue");
                        codeUrl = paymentFeignClient.getPayInfoFromWx(params);
                    }
                    case "1102" -> codeUrl = "支付宝";
                    case "1103" -> codeUrl = "余额";
                }
                //通过以上校验，说明订单存在
                //通过feign调用支付微服务模块，订单号，金额，描述
                if (codeUrl != null){
                    //设置 锁有效期 为 十分钟
                    redisTemplate.opsForValue().set(
                            "Order_Payinfo_Lock_User" + userId + "OrderNo_" + orderNo +"payway"+payway,
                            codeUrl,
                            10,
                            TimeUnit.SECONDS
                    );
                }
            }catch (Exception e){
                throw e;
            }finally {
                lock.unlock();
            }
        }
        return codeUrl;
    }

    /**
     * 校验订单
     *
     * @param orderInfoVo1 对同一个用户同一个类型的订单只能存在一个
     *                     设置key十分钟过期
     * @return
     */
    private boolean checkOrderInfoA(OrderInfoVo orderInfoVo1) {
        //获取用户
        Long userId = AuthContextHolder.getUserId();
        //获取类型:1001-专辑 1002-声音 1003-vip会员
        String itemType = orderInfoVo1.getItemType();
        //加分布式锁
        if (redisTemplate.opsForValue().setIfAbsent("SubmitOrder_" + userId + "_" + itemType,
                0,
                10,
                TimeUnit.MINUTES)) {
            return true;
        }
        return false;
    }

    /**
     * 保存订单减免表
     *
     * @param orderDerateVoList
     * @param orderId
     */
    private void saveOrderDerate(List<OrderDerateVo> orderDerateVoList, Long orderId) {
        OrderDerate orderDerate = new OrderDerate();
        orderDerateVoList.stream().forEach(OrderDerateVo -> {
            orderDerate.setOrderId(orderId);
            orderDerate.setDerateType(OrderDerateVo.getDerateType());
            orderDerate.setDerateAmount(OrderDerateVo.getDerateAmount());
            orderDerate.setRemarks(OrderDerateVo.getRemarks());
        });
        int insert = orderDerateMapper.insert(orderDerate);
        if (insert == 0) {
            throw new GuiguException(201, "订单减免表保存失败");
        }
    }

    /**
     * 保存订单明细
     *
     * @param orderDetailVoList
     * @param orderId
     */
    private void saveOrderDetail(List<OrderDetailVo> orderDetailVoList, Long orderId) {
        OrderDetail orderDetail = new OrderDetail();
        orderDetailVoList.stream().forEach(OrderDetailVo -> {
            orderDetail.setOrderId(orderId);
            orderDetail.setItemId(OrderDetailVo.getItemId());
            orderDetail.setItemName(OrderDetailVo.getItemName());
            orderDetail.setItemUrl(OrderDetailVo.getItemUrl());
        });
        int insert = orderDetailMapper.insert(orderDetail);
        if (insert == 0) {
            throw new GuiguException(201, "保存订单明细失败");
        }
    }


    /**
     * 获取已支付和未支付的订单信息
     *
     * @param trackId
     * @return
     */
    @Override
    public List<OrderInfo> getTrackOrderInfo(Long trackId) {
        //获取已支付和未支付的当前用户的声音订单
        List<OrderInfo> orderInfos = orderInfoMapper.selectList(new LambdaQueryWrapper<OrderInfo>()
                .eq(OrderInfo::getOrderStatus, "0901")
                .eq(OrderInfo::getOrderStatus, "0902")
                .eq(OrderInfo::getUserId, AuthContextHolder.getUserId())
                .eq(OrderInfo::getItemType, "0102"));
        return orderInfos;
    }

    /**
     * 获取用户未支付的订单信息
     *
     * @param userId
     * @return
     */
    @Override
    public Map<String, Object> getUserTrackOrderInfo(Long userId) {

        return null;
    }


    /**
     * 声音购买
     * @param itemId
     * @return
     */
    /**
     * 一个用户，一个声音只能被购买一次
     * 购买声音没有折扣
     * 如果有该购买记录或未支付订单不允许再次购买
     */
    private OrderInfoVo initTrackOrder(Long itemId) {
        //初始化OrderInfoVo对象
        OrderInfoVo orderInfoVo = new OrderInfoVo();

        return null;
    }

    /**
     * 专辑购买
     * @param itemId
     * @return
     */
    /**
     * 必须是付费专辑
     * 一个用户只能购买一张专辑
     * 会员购买有会员折扣，非会员有非会员折扣
     * 如果有该专辑的购买记录或未支付订单，则提示不允许再次购买
     */
    private OrderInfoVo initAlbumOrder(Long itemId) {
        //初始化OrderInfoVo对象
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        //获取专辑信息
        AlbumInfo albumInfo = albumClientFeign.getAlbumInfo(itemId);

        //判断是不是付费专辑
        if (albumInfo == null || albumInfo.getId() == null) {
            return null;
        }
        String payType = albumInfo.getPayType();
        if (payType.equals("0103")) {

            //判断该用户用没有买过该专辑或者有咩有创建该专辑的未支付订单，
            Integer integer = orderInfoMapper.selectAlbumOrder(itemId);
            if (integer != 0) throw new GuiguException(201, "已经买过了");
            //没买过
            //获取专辑原价格
            BigDecimal price = albumInfo.getPrice();
            //获取专辑折扣价
            BigDecimal discount = albumInfo.getDiscount();
            //获取专辑 会员折扣价
            BigDecimal vipDiscount = albumInfo.getVipDiscount();
            //orderInfoVo属性赋值
            orderInfoVo.setTradeNo(IdWorker.get32UUID());
            orderInfoVo.setItemType("1001");
            //专辑原价
            orderInfoVo.setOriginalAmount(price);

            //判断是不是会员
            boolean isVip = AuthContextHolder.getvipExpireTime() != null && AuthContextHolder.getvipExpireTime() > System.currentTimeMillis();
            //是vip使用会员折扣，非会员使用非会员折扣
            BigDecimal totalDiscount = isVip ? vipDiscount : discount;
            //订单总金额
            //判断专辑折扣是否等于 -1（不打折）
            BigDecimal orderAmount = totalDiscount.equals(BigDecimal.valueOf(-1))
                    //是就使用原价
                    ? price
                    //否则使用对应的折扣
                    : price.multiply(totalDiscount).divide(BigDecimal.valueOf(10));
            //专辑最终价格
            orderInfoVo.setOrderAmount(orderAmount);
            //减免金额
            orderInfoVo.setDerateAmount(orderInfoVo.getOriginalAmount().subtract(orderInfoVo.getOrderAmount()));
            //OrderDetailVo 赋值
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(itemId);
            orderDetailVo.setItemName("专辑购买" + albumInfo.getAlbumTitle());
            orderDetailVo.setItemUrl(albumInfo.getCoverUrl());
            orderDetailVo.setItemPrice(price.multiply(vipDiscount));
            orderInfoVo.setOrderDetailVoList(List.of(orderDetailVo));
            //OrderDerateVo赋值
            OrderDerateVo orderDerateVo = new OrderDerateVo();
            orderDerateVo.setDerateType("1405");
            orderDerateVo.setDerateAmount(price.subtract(price.multiply(vipDiscount)));
            orderDerateVo.setRemarks("专辑购买测试");

            orderInfoVo.setOrderDerateVoList(List.of(orderDerateVo));
            return orderInfoVo;
        }
        return null;
    }

    /**
     * 会员购订单
     *
     * @param itemId
     * @return
     */
    private OrderInfoVo initVipOrder(Long itemId) {
        //初始化OrderInfoVo对象
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        //调用用户微服务
        VipServiceConfig vipService = userClientFeign.getVipService(itemId);
        //orderDetailVo赋值
        OrderDetailVo orderDetailVo = new OrderDetailVo();
        orderDetailVo.setItemId(itemId);
        orderDetailVo.setItemName("Vip购买" + vipService.getName());
        orderDetailVo.setItemUrl(vipService.getImageUrl());
        orderDetailVo.setItemPrice(vipService.getDiscountPrice());
        List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
        orderDetailVoList.add(orderDetailVo);
        //orderInfoVo赋值
        orderInfoVo.setTradeNo(IdWorker.get32UUID());
        //支付方式
        orderInfoVo.setPayWay("1101");
        //付款项目类型
        orderInfoVo.setItemType("1003");
        //订单原始金额
        orderInfoVo.setOriginalAmount(vipService.getPrice());
        //减免总金额
        orderInfoVo.setDerateAmount(new BigDecimal(String.valueOf(vipService.getPrice())).subtract(vipService.getDiscountPrice()));
        //订单总金额
        orderInfoVo.setOrderAmount(vipService.getDiscountPrice());
        //订单明细列表
        orderInfoVo.setOrderDetailVoList(orderDetailVoList);
        //OrderDerateVo赋值
        OrderDerateVo orderDerateVo = new OrderDerateVo();
        orderDerateVo.setDerateType("1406");
        //减免金额
        orderDerateVo.setDerateAmount(orderInfoVo.getDerateAmount());
        //备注
        orderDerateVo.setRemarks("测试");
        List<OrderDerateVo> orderDerateVoList = new ArrayList<>();
        orderDerateVoList.add(orderDerateVo);

        //订单减免明细列表
        orderInfoVo.setOrderDerateVoList(orderDerateVoList);
        return orderInfoVo;
    }


//    public static void main(String[] args) {
//
//        long timeMillis = System.currentTimeMillis();
//
////        com.atguigu.tingshu.common.util.IdWorker idWorker = new com.atguigu.tingshu.common.util.IdWorker();
//        IdWorker.initSequence(1L,2L);
//
//        for (int i = 0; i < 1000000; i++) {
//
//            IdWorker.get32UUID();
////            idWorker.nextId();
//
//        }
//        System.out.println(System.currentTimeMillis() - timeMillis);
//    }
}
